package jutf8search;

import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * The class for dealing with reading files. Splits the input into chunks of size chunkLen.
 * ALso ensures that the chunks overlap each other by patternSize-1, so that a pattern occuring
 * over two chunks will not be missed.
 */
public class FileIO
{
    int patternSize;
    byte[] chunk;
    int chunkNum;
    int chunkLen;
    InputStream in;

    /**
     *  default constructor
     *  
     *  @param filename The file to be opened
     *  @param chunkSize  The size of the chunks that will be read (if 0, the default chunk size is used)
     *  @param patternSize The length of the pattern being searched for.
     *  
     *  @throws FileNotFoundException if the file specified cannot be found.
     */
    public FileIO(String filename, int chunkSize, int patternSize) throws FileNotFoundException 
    {
        this.patternSize = patternSize;
        this.chunk = new byte[chunkSize];
        chunkLen = chunkSize;
        chunkNum = 0;
        this.in = new FileInputStream(filename);
    }
    
    /**
     * Reads a query from a file and returns it as a byte array
     * 
     * @return A byte[] of the query
     */
    static public byte[] readQuery(String filename) throws IOException
    {
        InputStream pin = new FileInputStream(filename);
        byte[] pattern = new byte[pin.available()];
        pin.read(pattern);
        
        return pattern;
    }

    /**
     *  Reads a chunk of a file, where chunks are overlapped by patternSize
     *  returns a pointer to the chunk byte[]
     *  
     *  @return A byte[] with the bytes from the next chunk of the file
     */
    public byte[] readChunk() throws IOException 
    {
        byte[] buf, tmp;
        chunkNum++;
        if(chunkLen+patternSize < chunk.length) return null;
        if(chunkNum==1)
        {
            chunkLen = in.read(chunk,0,chunk.length);
            if(chunkLen < chunk.length){
            	byte[] temp = new byte[chunkLen];
            	System.arraycopy(chunk,0,temp,0,temp.length);
            	chunk = temp;
            }
        }
        else
        {
        	// only need to copy last m-1 characters of pattern to account for any overlap
            buf = new byte[chunk.length-patternSize+1];
            tmp = new byte[patternSize-1];
            chunkLen = in.read(buf,0,chunk.length-patternSize+1);
            if(chunkLen == -1) return null;
            // arraycopy(source, sourcePos, destination, destinationPos, numberOfElements)
            // copy end of last chunk into tmp array
            System.arraycopy(chunk,chunk.length-patternSize+1,tmp,0,patternSize-1);
            if(chunkLen < chunk.length-patternSize+1 && chunkLen != -1){
            	chunk = new byte[chunkLen+patternSize-1];
            	System.arraycopy(tmp,0,chunk,0,patternSize-1);
            	System.arraycopy(buf,0,chunk,patternSize-1,chunkLen);
            	return chunk;
            }
            // copy tmp onto beginning of chunk and then copy buf on end
            System.arraycopy(tmp,0,chunk,0,patternSize-1);
            System.arraycopy(buf,0,chunk,patternSize-1,buf.length);
        }
        return chunk;
    }
 
    /**
     * A class used in testing, generates a large file containing a repeating stream of bytes,
     * with name file.
     * 
     * @param file The file name to create.
     */
    public void createData(String file) throws IOException
    {
    	BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(file));
        byte[] b = new byte[]{65,66,67,68,69,70,71,72,73,74};
        int c = 1000000;
        for(int i=0; i<c; i++)
        {
           os.write(b);
           os.flush();
        }
   }

    /**
     * Main method. Used to test FileIO class on it's own. Creates a data file
     * using create data, and then reads it back in.
     * 
     * @param args The command line arguments. Unused.
     * @throws IOException When there is a problem reading or writing to the file.
     */
    public static void main(String[] args) throws IOException
    {
        FileIO io = new FileIO("data.dat",1024,100);
        io.createData("data.dat");
        @SuppressWarnings("unused") byte[] c = null;
        try
        {
            while((c = io.readChunk()) != null)
            {
                System.out.print("chunk"+io.chunkNum+"("+io.chunkLen+" bytes)");
                //for(int i=0; i<c.length; i++) { char a = (char) c[i]; System.out.print(a+" "); }
                System.out.println();
            }
        }
        catch(IOException e)
        { e.printStackTrace(); }
    }
}
